package com.by.my;


import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import java.sql.Statement;
import java.util.*;

/**
 * mybatis
 * 拦截器插件 并不是所有的拦截类中方法都能被拦截。 可以被拦截的所有的签名，如下
 */
@Intercepts({
	/*@Signature(//所有的insert update delete执行时调用
		type = Executor.class,
		method = "update",
		args = {MappedStatement.class, Object.class}
	),*/
	/*@Signature(//所有的select执行时调用
			type = Executor.class,
			method = "query",
			args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}
	),*/
    /*@Signature(//该方法只有查询的返回值类型为Cursor是被调用
        type = Executor.class,
        method = "queryCursor",
        args = {MappedStatement.class, Object.class, RowBounds.class}
    )*/
    /*@Signature(//该方法只有在通过SqlSession方法中调用 flushStatements 方法或者执行的接口方法中带有 @Flush 注解时才被调用
        type = Executor.class,
        method = "flushStatements",
        args = {}
    )*/
    /*@Signature(//该方法只有在通过SqlSession方法中调用 commit 方法时才被调用
        type = Executor.class,
        method = "commit",
        args = {boolean.class}
    )*/
	/*@Signature(//该方法只有在通过SqlSession方法中调用 rollback 方法时才被调用
		type = Executor.class,
		method = "rollback",
		args = {boolean.class}
	)*/
	/*@Signature(//该方法只有在通过SqlSession方法获取数据库连接时才被调用
		type = Executor.class,
		method = "getTransaction",
		args = {}
	)*/
	/*@Signature(//该方法只延迟加载获取新的Executor后才会被执行
		type = Executor.class,
		method = "close",
		args = {boolean.class}
	)*/
	/*@Signature(//该方法只在延迟加载执行查询方法前被执行
		type = Executor.class,
		method = "isClosed",
		args = {}
	)*/

	//================================================
	/*@Signature(//该方法只在执行存储过程处理出参的时候调用
		type = ParameterHandler.class,
		method = "getParameterObject",
		args = {}
	)*/
	/*@Signature(//该方法在所有数据库方法设置sql参数时被调用
		type = ParameterHandler.class,
		method = "getParameterObject",
		args = {PreparedStatement.class}
	)*/

	//================================================
	/*@Signature(//该方法会在除了存储过程及返回值类型为Cursor<T>(org.apache.ibatis.cursor.Cursor<T>)以外的查询方法中调用
				//对拦截处理Mybatis的查询结果非常有用，并且这个借口被调用的位置在处理二级缓存之前，因为通过这种方式处理的结果可以
				//执行二级缓存
		type = ResultSetHandler.class,
		method = "handleResultSets",
		args = {Statement.class}
	)*/
	/*@Signature(//3.4.0版本新增的方法
			// 该方法只在返回值类型为Cursor<T>的查询方法中被调用
		type = ResultSetHandler.class,
		method = "handleCursorResultSets",
		args = {Statement.class}
	)*/
	/*@Signature(// 该方法只在使用存储过程处理出参时被调用
		type = ResultSetHandler.class,
		method = "handleOutputParameters",
		args = {CallableStatement.class}
	)*/

	//================================================
	/*@Signature(//该方法会在数据库执行前被调用，优先于当前借口中的其他方法而被执行
		type = StatementHandler.class,
		method = "prepare",
		args = {Connection.class, Integer.class}
	)*/
	/*@Signature(// 该方法在 prepare 方法之后执行，用于处理参数信息
		type = StatementHandler.class,
		method = "parameterize",
		args = {Statement.class}
	)*/
	/*@Signature(// 设置全局配置 defaultExecutorType="BATCH" 时，执行数据操作才会被调用该方法
		type = StatementHandler.class,
		method = "batch",
		args = {Statement.class}
	)*/
	/*@Signature(// 在执行 select 方法是调用
		type = StatementHandler.class,
		method = "query",
		args = {Statement.class, ResultHandler.class}
	)*/
	/*@Signature(//3.4.0新增的 只在返回值类型为Cusor<T>的查询中被调用
		type = StatementHandler.class,
		method = "queryCursor",
		args = {Statement.class}
	)*/
})
@SuppressWarnings("all")
public class QueryIntercepter3 implements Interceptor {
    /**
     * https://blog.csdn.net/e_anjing/article/details/79102693
     * @param invocation
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
	    /*//当前被拦截的对象
    	Object target = invocation.getTarget();
    	//当前被拦截的方法
	    Method method = invocation.getMethod();
	    //被拦截方法中的参数
	    Object[] args = invocation.getArgs();
	    //
	    Object result = invocation.proceed();*/

    	/*
    	if(invocation.getTarget() instanceof Executor && invocation.getArgs().length == 2) {
		    MappedStatement ms = (MappedStatement)invocation.getArgs()[0];
		    BaseEntity baseEntity = (BaseEntity) invocation.getArgs()[1];
		    baseEntity.setInsetDatetime(new Date());
		    baseEntity.setUpdateDatetime(new Date());
	    }
	    */

    	/*if(invocation.getTarget() instanceof Executor && invocation.getArgs().length==4) {
		    String sql = getSqlByInvocation(invocation);
		    System.out.println("sql 查询拦截器:"+sql);
	    }*/

	    if(invocation.getTarget() instanceof Executor) {
		    Long startTime = System.currentTimeMillis();
		    Object object = invocation.proceed();
		    Long endTime = System.currentTimeMillis();
		    System.out.println("执行sql:" + getSqlByInvocation(invocation));
		    System.out.println("耗时:" + (endTime - startTime));

		    return object;
	    }

	    if(invocation.getTarget() instanceof  ResultSetHandler) {
		    //先得到执行的结果，再对结果进行处理
		    List<Object> list = (List<Object>)invocation.proceed();
		    for(Object object : list){
			    //如果结果是map类型的，就对map的可以进行转换
			    if(object instanceof Map) {
				    processMap((Map) object);
			    }else {
				    break;
			    }
		    }

		    return list;
	    }



        return null;
    }

    @Override
    public Object plugin(Object target) {

	    // 借助 Plugin 的 wrap(Object target,Interceptor interceptor); 包装我们的目标对象
	    // target: 目标对象, interceptor: 拦截器, this 表示使用当前拦截器
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
		String name = properties.getProperty("name", "");

    }

    private String getSqlByInvocation(Invocation invocation) {
        final Object[] args = invocation.getArgs();
        MappedStatement ms = (MappedStatement) args[0];
        Object parameterObject = args[1];
        BoundSql boundSql = ms.getBoundSql(parameterObject);
        return boundSql.getSql();
    }

    private void processMap(Map<String, Object> map) {
	    Set<String> keySet = new HashSet<String>(map.keySet());
	    for(String key : keySet) {
	    	//将以大写开头的字符串转换为小写，如果包含下划线也会处理为驼峰标识
		    //此处指通过这两个简单的标识来判断是否进行转换
		    if(key.charAt(0) >= 'A' && key.charAt(0) <= 'Z' || key.indexOf("_") >= 0 ) {
				Object value = map.get(key);
				map.remove(key);
				map.put(underlineToCamelhump(key), value);
		    }
	    }
    }

    public static String underlineToCamelhump(String inputString) {
    	StringBuilder sb = new StringBuilder();


    	boolean nextUpperCase = false;
    	for(int i=0, length=inputString.length(); i<length; i++) {
    		char c = inputString.charAt(i);
    		if(c=='_') {
    			if(sb.length()>0) {
    				nextUpperCase=true;
			    }
		    }else {
    			if(nextUpperCase) {
    				sb.append(Character.toUpperCase(c));
    				nextUpperCase = false;
			    }else {
    				sb.append(Character.toLowerCase(c));
			    }
		    }

	    }

    	return sb.toString();
    }

}
